package phaseframe

import (
	"strings"
	"time"

	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/cluster-api/util/annotations"
	ctrl "sigs.k8s.io/controller-runtime"

	bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/mergecluster"
	metricrecord "gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/metrics/record"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/clusterutil"
	l "gopkg.openfuyao.cn/cluster-api-provider-bke/utils/log"
)

// BasePhase is the base implementation of Phase, you can use it to implement your own phase
type BasePhase struct {
	// PhaseName is the name of the phase
	PhaseName confv1beta1.BKEClusterPhase
	// PhaseCName is the Chinese name of the phase
	PhaseCName string

	// Ctx is the context of the phase, it contains the needed information for the phase to execute
	Ctx *PhaseContext
	// Status is the status of the phase
	Status confv1beta1.BKEClusterPhaseStatus
	// StartTime is the start time of the phase
	StartTime metav1.Time

	// CustomPreHookFunc is the custom pre hook function,
	// it will be executed after the default pre hook but before report when use DefaultPreHook
	CustomPreHookFuncs []func(p Phase) error

	// CustomPostHookFunc is the custom post hook function,
	// it will be executed after the default post hook but before report when use DefaultPostHook
	CustomPostHookFuncs []func(p Phase, err error) error
}

// NewBasePhase returns a new BasePhase,you can use it to implement your own phase
func NewBasePhase(ctx *PhaseContext, phaseName confv1beta1.BKEClusterPhase) BasePhase {
	ctx.Log.NormalLogger = l.Named(phaseName.String()).With("bkecluster", utils.ClientObjNS(ctx.BKECluster))
	return BasePhase{
		PhaseName:           phaseName,
		Ctx:                 ctx,
		CustomPreHookFuncs:  make([]func(p Phase) error, 0),
		CustomPostHookFuncs: make([]func(p Phase, err error) error, 0),
	}
}

// DefaultPreHook is the default implementation of ExecutePreHook, use on demand
func (b *BasePhase) DefaultPreHook() error {
	// refresh bkecluster
	if err := b.Ctx.RefreshCtxBKECluster(); err != nil {
		return err
	}
	// refresh cluster, it's not necessary to refresh successfully
	_ = b.Ctx.RefreshCtxCluster()
	// set status and start time
	b.SetStatus(bkev1beta1.PhaseRunning)
	b.SetStartTime(metav1.Now())

	// run custom pre hook
	if b.CustomPreHookFuncs != nil && len(b.CustomPreHookFuncs) > 0 {
		for _, f := range b.CustomPreHookFuncs {
			if err := f(b); err != nil {
				return err
			}
		}
	}
	// report phase status
	return b.Report("", false)
}

// DefaultPostHook is the default implementation of ExecutePostHook, use on demand
func (b *BasePhase) DefaultPostHook(err error) error {

	if b.Name() != "EnsureDeleteOrReset" {
		defer metricrecord.PhaseDurationRecord(b.Ctx.BKECluster, b.CName(), b.StartTime.Time, err)
	}

	var msg string
	if err != nil {
		msg = err.Error()
	}
	if b.GetStatus() == bkev1beta1.PhaseSkipped {
		return b.Report(msg, false)
	}
	if err != nil {
		b.SetStatus(bkev1beta1.PhaseFailed)
		b.Ctx.Log.Debug("phase %q run failed: %v", b.Name(), err)
	} else {
		b.SetStatus(bkev1beta1.PhaseSucceeded)
		b.Ctx.Log.Debug("phase %q run succeeded", b.Name())
	}
	if b.CustomPostHookFuncs != nil && len(b.CustomPostHookFuncs) > 0 {
		for _, f := range b.CustomPostHookFuncs {
			if err := f(b, err); err != nil {
				return err
			}
		}
	}
	return b.Report(msg, false)
}

// DefaultNeedExecute is the default implementation of NeedExecute, use on demand
// it's only used for BKECluster type of 'bke'
func (b *BasePhase) DefaultNeedExecute(old *bkev1beta1.BKECluster, new *bkev1beta1.BKECluster) bool {
	// 对于删除的BKECluster，不执行
	if !new.DeletionTimestamp.IsZero() {
		return false
	}
	// 对于不是BKECluster，并且没有完全控制的，不执行
	if !clusterutil.IsBKECluster(new) && !clusterutil.FullyControlled(new) {
		return false
	}

	// 对于暂停的BKECluster，不执行
	if new.Spec.Pause || annotations.HasPaused(new) {
		return false
	}
	// 对于DryRun的BKECluster，不执行
	if new.Spec.DryRun {
		return false
	}

	if strings.HasSuffix(string(new.Status.ClusterHealthState), "Failed") {
		return false
	}

	return true
}

// NormalNeedExecute differs from DefaultNeedExecute is for all type of BKECluster
func (b *BasePhase) NormalNeedExecute(old *bkev1beta1.BKECluster, new *bkev1beta1.BKECluster) bool {
	// 对于删除的BKECluster，不执行
	if !new.DeletionTimestamp.IsZero() {
		return false
	}
	// 对于暂停的BKECluster，不执行
	if new.Spec.Pause || annotations.HasPaused(new) {
		return false
	}
	// 对于DryRun的BKECluster，不执行
	if new.Spec.DryRun {
		return false
	}
	if strings.HasSuffix(string(new.Status.ClusterHealthState), "Failed") {
		return false
	}
	return true
}

// NeedExecute is the default implementation of NeedExecute.
// By comparing the status of old and new, determine whether the current stage needs to be executed
func (b *BasePhase) NeedExecute(old *bkev1beta1.BKECluster, new *bkev1beta1.BKECluster) (needExecute bool) {
	return b.DefaultNeedExecute(old, new)
}

// ExecutePreHook is the default implementation of ExecutePreHook.
// It is called before the execution of the current phase
func (b *BasePhase) ExecutePreHook() error {
	return b.DefaultPreHook()
}

// RegisterPreHooks is used to register a custom pre hook function
func (b *BasePhase) RegisterPreHooks(hooks ...func(p Phase) error) {
	b.CustomPreHookFuncs = append(b.CustomPreHookFuncs, hooks...)
}

// RegisterPostHooks is used to register a custom post hook function
func (b *BasePhase) RegisterPostHooks(hooks ...func(p Phase, err error) error) {
	b.CustomPostHookFuncs = append(b.CustomPostHookFuncs, hooks...)
}

// ExecutePostHook is the default implementation of ExecutePostHook.
// It is called after the execution of the current phase
func (b *BasePhase) ExecutePostHook(err error) error {
	return b.DefaultPostHook(err)
}

// Execute is the default implementation of Execute.
// It is called when the current phase is needed to be executed
func (b *BasePhase) Execute() (result ctrl.Result, err error) {
	panic("implement me")
}

// Name returns the name of the current phase
func (b *BasePhase) Name() confv1beta1.BKEClusterPhase {
	return b.PhaseName
}

func (b *BasePhase) CName() string {
	if b.PhaseCName == "" {
		return b.PhaseName.String()
	}
	return b.PhaseCName
}

func (b *BasePhase) SetCName(name string) {
	b.PhaseCName = name
}

// Report is used to report the status on the PhaseContext.BKECluster.Status.PhaseStatus
// todo bug -> 避免一个phase重复，应该更新开始时间和结束时间
func (b *BasePhase) Report(msg string, onlyRecord bool) error {
	_, c, bkeCluster, _, log := b.Ctx.Untie()

	// 没有状态不上报，说明不执行，也不需要在状态中展示
	if b.Status == "" {
		return nil
	}
	status := bkeCluster.Status.PhaseStatus

	defer func() {
		bkeCluster.Status.PhaseStatus = status
		if onlyRecord {
			return
		}
		if err := mergecluster.SyncStatusUntilComplete(c, bkeCluster); err != nil {
			log.NormalLogger.Errorf("Failed to update BKECluster status: %v", err)
		}
	}()

	var ps confv1beta1.PhaseState
	// 如果是跳过，找到waiting或者running的状态，更新为skipped,且置0start-time和end-time，否则追加
	if b.Status == bkev1beta1.PhaseSkipped {
		// 反向遍历status，如果已经存在，且是等待或者执行中，原地更新为跳过,且置0start-time和end-time
		for i := len(status) - 1; i >= 0; i-- {
			ps = *status[i].DeepCopy()
			if ps.Name == b.PhaseName && (ps.Status == bkev1beta1.PhaseWaiting || ps.Status == bkev1beta1.PhaseRunning) {
				ps.Status = bkev1beta1.PhaseSkipped
				ps.StartTime = nil
				ps.EndTime = nil
				status[i] = ps
				return nil
			}
		}
		ps = confv1beta1.PhaseState{
			Name:   b.PhaseName,
			Status: bkev1beta1.PhaseSkipped,
		}
		status = append(status, ps)
		return nil
	}

	// 如果b.startTime为空，或者b.status为skipped || waiting，构建新的b.status，并追加
	if b.StartTime.IsZero() || b.Status == bkev1beta1.PhaseWaiting {
		// 反向遍历status
		for i := len(status) - 1; i >= 0; i-- {
			ps = *status[i].DeepCopy()
			// 如果已经存在，且是执行失败，追加
			if ps.Name == b.PhaseName && ps.Status == bkev1beta1.PhaseFailed {
				break
			}
			// 如果已经存在，且是执行成功，原地更新为等待或跳过,且置0start-time和end-time
			if ps.Name == b.PhaseName && ps.Status == bkev1beta1.PhaseSucceeded {
				ps.Status = b.Status
				ps.StartTime = nil
				ps.EndTime = nil
				status[i] = ps
				return nil
			}
			// 如果已经存在，且是等待或者执行中，原地更新为等待或跳过,且置0start-time和end-time
			if ps.Name == b.PhaseName && (ps.Status == bkev1beta1.PhaseWaiting || ps.Status == bkev1beta1.PhaseRunning) {
				ps.Status = b.Status
				ps.StartTime = nil
				ps.EndTime = nil
				status[i] = ps
				return nil
			}
		}

		ps = confv1beta1.PhaseState{
			Name:   b.PhaseName,
			Status: b.Status,
		}
		status = append(status, ps)
		return nil
	}

	// 正在运行的phase，还上报b.startTime
	if b.Status == bkev1beta1.PhaseRunning {
		// 设置 bkecluster 当前的phase
		bkeCluster.Status.Phase = b.PhaseName
		// 找到已经存在的b.status，如果存在，原地更新
		for i := len(status) - 1; i >= 0; i-- {
			ps = *status[i].DeepCopy()
			if ps.Name == b.PhaseName && ps.Status == bkev1beta1.PhaseWaiting || ps.Status == bkev1beta1.PhaseRunning {
				ps.Status = bkev1beta1.PhaseRunning
				ps.StartTime = &b.StartTime
				status[i] = ps
				return nil
			}
		}
		// 没有找到，追加
		ps = confv1beta1.PhaseState{
			Name:      b.PhaseName,
			Status:    bkev1beta1.PhaseRunning,
			StartTime: &b.StartTime,
		}
		status = append(status, ps)
		return nil
	}
	// 已经结束的phase，还上报b.startTime和phaseEndTime,并原地更新
	if b.Status == bkev1beta1.PhaseFailed || b.Status == bkev1beta1.PhaseSucceeded {
		for i := len(status) - 1; i >= 0; i-- {
			ps = *status[i].DeepCopy()
			if ps.Name == b.PhaseName && (ps.Status == bkev1beta1.PhaseRunning || ps.Status == bkev1beta1.PhaseWaiting) {
				ps.Status = b.Status
				ps.StartTime = &b.StartTime
				ps.EndTime = &metav1.Time{Time: time.Now()}
				ps.Message = msg

				status[i] = ps
				return nil
			}
		}

		ps = confv1beta1.PhaseState{
			Name:      b.PhaseName,
			Status:    b.Status,
			StartTime: &b.StartTime,
			EndTime:   &metav1.Time{Time: time.Now()},
		}
		status = append(status, ps)
		return nil
	}
	return nil
}

// SetStatus is used to set the status of the current phase
func (b *BasePhase) SetStatus(status confv1beta1.BKEClusterPhaseStatus) {
	b.Status = status
}

// GetStatus is used to get the status of the current phase
func (b *BasePhase) GetStatus() confv1beta1.BKEClusterPhaseStatus {
	return b.Status
}

// SetStartTime is used to set the startTime of the current phase
func (b *BasePhase) SetStartTime(startTime metav1.Time) {
	b.StartTime = startTime
}

// GetStartTime is used to get the startTime of the current phase
func (b *BasePhase) GetStartTime() metav1.Time {
	return b.StartTime
}

// GetPhaseContext is used to get the PhaseContext of the current phase
func (b *BasePhase) GetPhaseContext() *PhaseContext {
	return b.Ctx
}

// SetPhaseContext is used to set the PhaseContext of the current phase
func (b *BasePhase) SetPhaseContext(ctx *PhaseContext) {
	b.Ctx = ctx
}
